import platform = require('../common/baseCommon');
import appCommon = require('../common/appCommon');
import fs = require("fs");
import express = require('express');
import http = require('http');
import https = require('https');
import path = require('path');

import md5 = require('md5');
import sha1 = require('sha1');
import crypto = require('crypto');
import { debug } from 'util';
import { read } from 'fs';

const moduleName = "platform.server";

/**
 * 全局服务列表
 */
export class globalServices {

};

/**
 * 服务信息: 包含服务名称和服务对象的信息
 */
@platform.addon("服务信息",
    "包含服务名称和服务对象的信息",
    moduleName)
export class serviceInfo {
    constructor(public serviceName?: string
        , public serviceObject?: platform.baseObject) {

    }
}
/**
 * 注册服务列表
 * @param serviceInfos 服务信息列表
 */
export function registerServices(serviceInfos: serviceInfo[]) {
    for (var serviceInfo of serviceInfos) {
        registerService(serviceInfo);
    }
}

/**
 * 注册服务
 * @param serviceInfo 服务信息
 */
export function registerService(serviceInfo: serviceInfo) {
    if (!globalServices[serviceInfo.serviceName]) {
        serviceInfo.serviceObject.$id = serviceInfo.serviceName;
        globalServices[serviceInfo.serviceName] = serviceInfo.serviceObject;
    }
}

/**
 * 请求
 * @param remoteRequest 远程请求
 * @returns 远程调用方法的结果
 */
export function request(request: platform.remoteRequest, rqHeader: platform.remoteHeader): any {
    let service = globalServices[request.serviceName];
    if (!service)
        throw new Error(`远程调用服务${request.serviceName}异常，服务找不到。`);

    let serviceFunc = service[request.functionName];
    if (!serviceFunc)
        throw new Error(`远程调用服务${request.serviceName}异常，方法${request.functionName}找不到。`);
    service.rqHeader = rqHeader;
    let result = serviceFunc.apply(service, request.args);

    /** 设置响应头，并同步请求头信息 */
    if (result && (result instanceof platform.ResponseMessage) &&
        request.args && request.args[0] && (request.args[0] instanceof platform.RequestMessage)) {
        let requestMessage: platform.RequestMessage<any> = <platform.RequestMessage<any>>request.args[0];
        let responseMessage: platform.ResponseMessage<any> = <platform.ResponseMessage<any>>result;
        responseMessage.header.tranID = requestMessage.header.tranID;
        responseMessage.header.userToken = requestMessage.header.userToken;
        responseMessage.header.expand = requestMessage.header.expand;
    }
    return result;
};

/**
 * 远程调用
 * @param req 请求
 * @param res 响应
 */
export function remoteCall(req: express.Request, res: express.Response) {
    // 定义了一个post变量，用于暂存请求体的信息
    let buffer = [];
    // 通过req的data事件监听函数，每当接受到请求体的数据，就累加到post变量中
    req.on('data', function (chunk) {
        buffer.push(chunk);
    });
    // utility.session = data;//{name,pass,}
    // 在end事件触发后，通过querystring.parse将post解析为真正的POST请求格式，然后向客户端返回。
    req.on('end', function () {
        //res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
        try {
            var dataBuf = Buffer.concat(buffer);
            let post = dataBuf.toString('utf8');
            // 对请求数据进行解码,防止乱码
            let temp = decodeURIComponent(post);
            let requestObj = platform.addonDeserialize(temp);
            let result = request(requestObj, requestObj.header);
            if (result &&
                result.constructor &&
                result.constructor.prototype.then) {
                result.then((values: any) => {
                    let response = platform.addonSerialize({
                        d: values
                    });
                    res.write(response);
                    res.end();
                }).catch(error => {
                    res.status(600).send(`${error.message}`).end();
                });
            } else {
                let response = platform.addonSerialize({
                    d: result
                });
                res.write(response);
                res.end();
            }
        } catch (error) {
            res.status(600).send(`${error.message}`).end();
        }
    });
};

/**
 * 服务特性控制器
 */
export class serviceFeatureController extends platform.baseFeatureController {
    /**
    * 初始化特性列表
    */
    initFeatures?(targetOject: platform.IFeatureSupport): Promise<any> {
        if (targetOject && targetOject.featureController) {
            var serviceObject: platform.baseObject = targetOject;
            this.target = serviceObject.$id;
        }
        if (!this.features) {
            this.features = [];
            let dataService: platform.IDataService = globalServices["webptDataService"];
            return dataService.query("cmd_featureByModule", { "TARGER_ID": this.target, "TARGER_TYPE": "ServiceMethod" })
                .then((table: platform.dataTable) => {
                    table.rows.forEach(element => {

                        let ft: platform.feature = new platform.feature();
                        ft.owner = element.rowValues["ROLE_ID"];
                        ft.ownerType = element.rowValues["OWNER_TYPE"];
                        ft.targetType = element.rowValues["TARGER_TYPE"];
                        ft.content = element.rowValues["TARGER_CONTENT"];
                        ft.effect = element.rowValues["FEATURE"];
                        this.features.push(ft);

                    });

                    return;
                }).catch(error => {
                    throw new Error(`获取特性控制信息异常，发生异常:${error.message}`);
                });
        }
        else {
            return Promise.resolve();
        }
    }

    /**
     * 获取生效的特性
     */
    getFeature?(uinfo: platform.userToken, ownertype: string, content: string): Promise<platform.feature> {
        return this.initFeatures(null)
            .then(() => {
                let ftMethod: platform.feature[] = this.features.map<platform.feature>((item: platform.feature, index: number) => {
                    if (item.content == content && item.ownerType == ownertype) {
                        return item;
                    }
                });
                var currentFeature: platform.feature;//生效的权限

                ftMethod.forEach(element => {
                    for (var index = 0; index < uinfo.userinfo.roles.length; index++) {
                        if (element.owner == uinfo.userinfo.roles[index].id) {//如果用户拥有控制该特性的角色
                            if (!currentFeature || currentFeature.level > element.level) {//如果当前级别高于原来的级别
                                currentFeature = element;
                                continue;
                            }
                        }

                    }
                });

                return currentFeature;
            });
    }
}

@platform.addon("数据库日志记录器",
    "将信息记录到数据库的日志记录器",
    moduleName)
export class databaseLogger extends platform.baseLogger {
    /**
     * 记录信息
     * @param date 日期
     * @param message 消息 
     * @param infoType 消息类型，可选的值：error: 错误；warning：警告；information：信息
     */
    log(date: Date, message: string, infoType: "error" | "warning" | "information" = "information"): void {
        // TODO: 改成记录到数据库
        platform.logToConsole(date, message, infoType);
    }
}

@platform.addon("文件式日志记录器",
    "将信息记录到文件的日志记录器",
    moduleName)
export class fileLogger extends platform.baseLogger {
    /**
     * 记录信息
     * @param date 日期
     * @param message 消息 
     * @param infoType 消息类型，可选的值：error: 错误；warning：警告；information：信息
     */
    log(date: Date, message: string, infoType: "error" | "warning" | "information" = "information"): void {
        // TODO: 改成记录到文件
        platform.logToConsole(date, message, infoType);
    }
}


/**
 * https 配置
 */
@platform.addon("https 配置", "https 配置", moduleName)
export class httpsConfig extends platform.baseObject {
    //端口
    port: number = 3001;
    //私钥文件路径(所以服务器目录相对路径)
    privateKeyPath: string = '';
    //证书路径(所以服务器目录相对路径)
    certificatePath: string = '';
    //公私钥编码
    codogram: string = 'uft-8';
}

/**
 * 加密工具类
 */
export class encrypt {
    /**
     * 计算MD5值 (32位)
     * @param value 
     */
    static calMd5(value: string | Buffer): string {
        return md5(value);
    }
    /**
     * 计算sha值(40位小写)
     * @param message 
     * @param options 
     */
    static calSha1(message: string | Buffer, options?: any): string | Uint8Array {
        return sha1(message, options);
    }
    /**
     * aes 加密 （只支持256位）
     * @param key 加密的密钥（必须是32位）
     * @param iv 向量（16位）
     * @param data 加密的数据
     */
    static encryptAES(key: any, iv: any, data: any): string {
        var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
        var crypted = cipher.update(data, 'utf8', 'binary');
        crypted += cipher.final('binary');
        crypted = new Buffer(crypted, 'binary').toString('base64');
        return crypted;
    }
    /**
     * 解密aes(只支持256位)
     * @param key 加密的密钥(必须是32位)
     * @param iv 向量（16位）
     * @param crypted 密文 
     */
    static decryptAES(key: any, iv: any, crypted: any) {
        crypted = new Buffer(crypted, 'base64').toString('binary');
        var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
        var decoded = decipher.update(crypted, 'binary', 'utf8');
        decoded += decipher.final('utf8');
        return decoded;
    }
}

/**
 * 主应用程序
 */
export let mainApplication: serverApplication;
/**
 * 服务应用
 */
@platform.addon("服务应用设置", "包含各种资源的主服务应用实体对象", moduleName)
export class serverApplication extends appCommon.application {
    /**
     * 服务列表
     */
    services: serviceInfo[];
    /**
     * https配置
     */
    httpsConfig: httpsConfig;
    /**
     * 服务端口
     */
    port: number = 3000;

    constructor(port?: number, services?: serviceInfo[]) {
        super();
        this.port = port;
        this.services = services;
    }

    start(): void {
        super.start();
        mainApplication = this;
        if (this.services) {
            registerServices(this.services);
        }
    }
}


/**
 * 保存
 * @param filePath 文件路径
 */
export function saveObject(filePath: string, obj: platform.baseObject): void {
    let configString = platform.addonSerialize(obj);
    fs.writeFileSync(filePath, configString);

    platform.log(`配置文件:${filePath}保存完毕`);
}

/**
 * 读取
 * @param filePath 文件路径
 */
export function loadObject(filePath: string): any {
    let buffer = fs.readFileSync(filePath);
    let obj = platform.addonDeserialize(buffer.toString());

    platform.log(`配置文件:${filePath}读取完毕`);

    return obj;
}

/**
 * 准备配置
 * @param defaultConfig 默认配置对象
 * @param configFile 配置文件
 * @param loadConfig 是否从配置文件加载
 * @param saveConfig 是否保存配置文件
 */
export function prepareConfig(defaultConfig: platform.baseObject,
    configFile: string, loadConfig: boolean = false, saveConfig: boolean = true): platform.baseObject {
    let configObject: platform.baseObject;
    // 加载应用程序配置
    if (loadConfig) {
        configObject = loadObject(configFile);
    } else {
        configObject = defaultConfig;
        if (saveConfig) {
            saveObject(configFile, configObject);
        }
    }
    return configObject;
}

/**
 * 启动服务
 * @param app Express应用
 * @param serverApp 应用程序 
 * @param configFile 配置文件
 * @param saveConfig 是否保存配置
 */
export function startServer(app: express.Express, serverApp: serverApplication, beforeStart?: () => void) {

    //todo 测试数据连接器
    serverApp.start();

    // 设置端口
    app.set('port', process.env.port || serverApp.port);
    app.set('trust proxy', true);
    // 注册远程调用服务
    app.post('/remoteCall', remoteCall);

    // 执行启动事件
    if (beforeStart) {
        beforeStart();
    }

    if (serverApp.httpsConfig) {
        try {
            //读取公私钥文件
            var privateKey = fs.readFileSync(path.join(__dirname, serverApp.httpsConfig.privateKeyPath), 'utf8');
            var certificate = fs.readFileSync(path.join(__dirname, serverApp.httpsConfig.certificatePath), 'utf8');
            var credentials = { key: privateKey, cert: certificate };
            //启动https 服务
            https.createServer(credentials, app).listen(serverApp.httpsConfig.port, function () {
                platform.log('https服务启动，监听端口：' + serverApp.httpsConfig.port);
            });
        } catch (error) {
            platform.log(error);
        }
    }
    // 启动服务
    http.createServer(app).listen(app.get('port'), function () {
        platform.log('http服务启动，监听端口：' + app.get('port'));
    });
}